#!/usr/bin/env python

import argparse
import datetime
import os
import os.path
import sys

from jinja2 import Environment, FileSystemLoader
from shutil import copyfile
from subprocess import call

PIPELINES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pipelines')

TEMPLATE_ENVIRONMENT = Environment(
    autoescape=False,
    loader=FileSystemLoader(os.path.join(PIPELINES_DIR, 'templates')),
    trim_blocks=True,
    lstrip_blocks=True,
    variable_start_string='[[',  # 'default {{ has conflict with pipeline syntax'
    variable_end_string=']]',
    extensions=['jinja2.ext.loopcontrols'])


class PxfParser(argparse.ArgumentParser):
    def error(self, message):
        sys.stderr.write('error: %s\n' % message)
        # self.print_help(sys.stderr)
        sys.exit(2)


def render_template(template_filename, context):
    """Render template"""
    return TEMPLATE_ENVIRONMENT.get_template(template_filename).render(context)


def create_pipeline():
    context = {
        'template_filename': ARGS.template_filename,
        'generator_filename': os.path.basename(__file__),
        'timestamp': datetime.datetime.now(),
        'pipeline_type': ARGS.pipeline_type,
        'compile_gpdb': ARGS.compile_gpdb,
        'acceptance': ARGS.acceptance,
        'gpdb_type': ARGS.gpdb_branch,
        'gpdb_branch': ARGS.actual_gpdb_branch
    }

    pipeline_yml = render_template(ARGS.template_filename, context)

    with open(ARGS.output_filepath, 'w') as output:
        header = render_template('header.yml', context)
        output.write(header + '\n')
        output.write(pipeline_yml)

    return True


def how_to_use_generated_pipeline_message():
    m = '\n'
    m += '======================================================================\n'
    m += '  Generate Pipeline type: .. : %s\n' % ARGS.pipeline_type
    m += '  Pipeline file ............ : %s\n' % ARGS.output_filepath
    m += '  Template file ............ : %s\n' % ARGS.template_filename
    m += '======================================================================\n\n'

    cmd = 'fly -t ' + ARGS.TARGET + ' set-pipeline \\\n'
    cmd += '    -c ' + ARGS.output_filepath + ' \\\n'
    cmd += '    -l ~/workspace/gp-continuous-integration/secrets/gpdb_common-ci-secrets.yml \\\n'
    cmd += '    -l ~/workspace/pxf/concourse/settings/pxf-multinode-params.yml \\\n'
    cmd += '    -l ~/workspace/gp-continuous-integration/secrets/ccp_ci_secrets_ud.yml \\\n'
    cmd += '    -l ~/workspace/gp-continuous-integration/secrets/ccp_ci_secrets_ud_kerberos.yml \\\n'

    if ARGS.pipeline_type == 'pxf':
        cmd += '    -l ' + ARGS.YML_FILE + ' \\\n'
    elif ARGS.pipeline_type == 'release':
        if ARGS.gpdb_branch == '5X_STABLE':
            cmd += '    -l ~/workspace/gp-continuous-integration/secrets/pxf-release-5x.prod.yml \\\n'
        else:
            # 6x and master share the same secrets at the moment
            cmd += '    -l ~/workspace/gp-continuous-integration/secrets/gpdb-6X_STABLE-release-secrets.prod.yml \\\n'
            cmd += '    -l ~/workspace/gp-continuous-integration/secrets/pxf-release-6x.prod.yml \\\n'
            cmd += '    -v gcs-bucket-resources-prod=pivotal-gpdb-concourse-resources-prod\\\n'

    cmd += '    -v folder-prefix=' + ARGS.PREFIX + ' -v test-env=' + ARGS.TEST_ENV + ' \\\n'
    if ARGS.pipeline_type == 'pxf' and not ARGS.compile_gpdb:
        cmd += '    -v icw_green_bucket=' + ARGS.ICW_GREEN_BUCKET + ' \\\n'
        if ARGS.gpdb_branch != '5X_STABLE':
            cmd += '    -v gcs-bucket-intermediates=pivotal-gpdb-concourse-resources-intermediates-prod \\\n'
            cmd += '    -v gcs-bucket-resources-prod=pivotal-gpdb-concourse-resources-prod\\\n'
    if ARGS.compile_gpdb:
        cmd += '    -v configure_flags=--enable-cassert -v gpaddon-git-branch=master \\\n'
    cmd += '    -v gpdb-branch=' + ARGS.actual_gpdb_branch + ' -v pgport=' + ARGS.PGPORT
    if ARGS.pipeline_name:
        cmd += '    -p ' + ARGS.pipeline_name
    else:
        cmd += '    -p ' + ARGS.PIPELINE_NAME

    m += cmd

    return m, cmd.replace('\\\n', '').split()


def build_parser(output_filename):
    pxf_parser = PxfParser(
        description='Generate Concourse Pipeline utility',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    pxf_parser.add_argument('deploy_env',
                            action='store',
                            choices=['prod', 'dev'],
                            help='Deploy environment ("prod" or "dev")')

    pxf_parser.add_argument('gpdb_branch',
                            action='store',
                            choices=['master', '5x', '6x'],
                            help='GPDB-compatible version ("master", "5x", "6x")')

    pxf_parser.add_argument('feature',
                            nargs="?",
                            action='store',
                            default="master",
                            help='PXF Branch')

    # When you compile your own GPDB branch
    pxf_parser.add_argument('-b', '--branch',
                            action='store',
                            dest='actual_gpdb_branch',
                            help='GPDB branch')

    pxf_parser.add_argument('-T', '--template',
                            action='store',
                            dest='template_filename',
                            default="pxf-tpl.yml",
                            help='Name of template to use, in templates/')

    pxf_parser.add_argument('-o', '--output',
                            action='store',
                            dest='output_filepath',
                            default=os.path.join(PIPELINES_DIR, output_filename),
                            help='Output filepath')

    pxf_parser.add_argument('-p', '--pipeline_type',
                            action='store',
                            dest='pipeline_type',
                            choices=['pxf', 'release'],
                            default='pxf',
                            help='Pipeline type ("pxf" or "release")')

    pxf_parser.add_argument('-c', '--compile_gpdb',
                            action='store_true',
                            dest='compile_gpdb',
                            default=False,
                            help='Adds a compile GPDB job to the pipeline')

    pxf_parser.add_argument('-u', '--user',
                            action='store',
                            dest='user',
                            default=os.getlogin(),
                            help='Developer userid to use for pipeline file name.')

    pxf_parser.add_argument('-n', '--pipeline_name',
                            action='store',
                            dest='pipeline_name',
                            help='Name of pipeline to use.')

    pxf_parser.add_argument('-a', '--acceptance',
                            action='store_true',
                            default=False,
                            dest='acceptance',
                            help='Create an acceptance pipeline.')
    return pxf_parser


if __name__ == "__main__":
    default_output_filename = "pxf_pipeline-generated.yml"
    PARSER = build_parser(default_output_filename)
    ARGS = PARSER.parse_args()
    ARGS.PGPORT = '15432'

    if ARGS.gpdb_branch == '5x':
        ARGS.gpdb_branch = '5X_STABLE'
        ARGS.ICW_GREEN_BUCKET = 'gpdb5-stable-concourse-builds'
    elif ARGS.gpdb_branch == '6x':
        ARGS.gpdb_branch = '6X_STABLE'
        ARGS.ICW_GREEN_BUCKET = 'gpdb5-assert-concourse-builds'
        ARGS.PGPORT = '6000'
    elif ARGS.gpdb_branch == 'master':
        ARGS.ICW_GREEN_BUCKET = 'gpdb5-assert-concourse-builds'
        ARGS.PGPORT = '7000'
    else:
        sys.stderr.write('error: Only master, 6x and 5x branches are supported for GPDB.'
                         'Include --compile_gpdb flag if you intend to compile GPDB from a different branch.\n')
        sys.exit(1)

    if not ARGS.actual_gpdb_branch:
        ARGS.actual_gpdb_branch = ARGS.gpdb_branch

    if ARGS.pipeline_type == 'release' and os.path.basename(ARGS.output_filepath) == default_output_filename:
        default_output_filename = '6x-release_pipeline-generated.yml'
        if ARGS.gpdb_branch == '5X_STABLE':
            default_output_filename = '5x-release_pipeline-generated.yml'
        ARGS.output_filepath = os.path.join(PIPELINES_DIR, default_output_filename)

    if ARGS.deploy_env != 'prod' and os.path.basename(ARGS.output_filepath) == default_output_filename:
        default_dev_output_filename = ARGS.pipeline_type + '_pipeline-dev-' + ARGS.user + '.yml'
        ARGS.output_filepath = os.path.join(PIPELINES_DIR, default_dev_output_filename)

    ARGS.DEFAULT_YML_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'default.yml')
    ARGS.YML_FILE = ARGS.DEFAULT_YML_FILE

    # dev pipeline
    if ARGS.deploy_env == 'dev':
        ARGS.CONFIG = ARGS.user
        if ARGS.feature != 'master':
            ARGS.CONFIG = ARGS.CONFIG + '-' + ARGS.feature
        ARGS.YML_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), '.' + ARGS.CONFIG + '.yml')
        ARGS.TEST_ENV = 'dev'
        ARGS.PREFIX = 'dev/' + ARGS.CONFIG
        ARGS.PIPELINE_NAME = 'dev:' + ARGS.pipeline_type + '_' + ARGS.CONFIG + '_' + ARGS.gpdb_branch

        if not os.path.exists(ARGS.YML_FILE):
            if ARGS.feature == 'master':
                copyfile(ARGS.DEFAULT_YML_FILE, ARGS.YML_FILE)
            else:
                with open(ARGS.DEFAULT_YML_FILE, 'r') as f, open(ARGS.YML_FILE, 'w') as o:
                    for line in f:
                        if 'pxf-git-branch: master' in line:
                            o.write('pxf-git-branch: ')
                            o.write(ARGS.feature)
                        elif 'gpdb-git-branch: master' in line:
                            o.write('gpdb-git-branch: ')
                            o.write(ARGS.actual_gpdb_branch)
                        else:
                            o.write(line)

    # prod pipeline
    else:
        ARGS.PREFIX = 'prod/gpdb_branch'
        ARGS.PIPELINE_NAME = 'pxf_' + ARGS.gpdb_branch
        ARGS.TEST_ENV = ''

    if ARGS.pipeline_type == 'release' and ARGS.deploy_env == 'prod':
        ARGS.TARGET = 'ud'
        if ARGS.gpdb_branch == '6X_STABLE':
            ARGS.PIPELINE_NAME = 'pxf_release_6x'
        elif ARGS.gpdb_branch == '5X_STABLE':
            ARGS.PIPELINE_NAME = 'pxf_release_5x'
        else:
            sys.stderr.write('error: Only 6x and 5x branches are supported for GPDB release.\n')
            sys.exit(1)
    else:
        ARGS.TARGET = 'ud'

    pipeline_created = create_pipeline()

    if pipeline_created:
        msg, cmd_list = how_to_use_generated_pipeline_message()
        print(msg)
        # Expand all home directory paths (i.e. ~/workspace...)
        cmd_list = [os.path.expanduser(p) if p[0] == '~' else p for p in cmd_list]
        call(cmd_list)
    else:
        sys.exit(1)
